/*
示例如下：

#include "MyWifiMqttManager.h"

//要订阅的mqtt地址
String mySubTopics[] = {
  "my/esp/test1",
  "my/esp/test2"
};

//mqtt 收到消息后的callback
void mqttCallback(char* topic, byte* payload, unsigned int length) {
  //打印接收到的消息
  Serial.print("[MyWifiMqttManager] Message received on topic: ");
  Serial.println(topic);
  Serial.print("[MyWifiMqttManager] Message: ");
  //赋值给char*
  char payloadChars[length];
  for (int i = 0; i < length; i++) {
    payloadChars[i] = (char)payload[i];
  }
  payloadChars[length] = '\0';
  Serial.println(payloadChars);
}

void setup() {
  //初始化串口
  Serial.begin(115200);
  //重新设置wifi/mqtt(适合于开头测试使用)
  //clearConfig();
  //可选设置
  // setWifiHostname("esp32-test");
  // setMqttClientId("esp32-test");
  //初始化wifi和mqtt
  initWifiMqtt("esp32-test", "12345678", "my/esp/online", mySubTopics, 2, mqttCallback);
}

void loop() {
  //mqtt循环获取值
  mqttLoop();
}

*/

//引入头文件
//存储
#ifdef ARDUINO_ARCH_ESP32
  #include "SPIFFS.h"
#elif defined(ARDUINO_ARCH_ESP8266)
  #include <FS.h>
#endif
//wifi配置
#include <WiFiManager.h>
//json处理
#include <ArduinoJson.h>
//wifi
#ifdef ARDUINO_ARCH_ESP32
  #include <WiFi.h>
#elif defined(ARDUINO_ARCH_ESP8266)
  #include <ESP8266WiFi.h>
#endif
//mqtt
#include <PubSubClient.h>

//wifi配置对象创建
WiFiManager wifiManager;

//自定义静态内容
char* apSsid = "";
char* apPassword = "";
char* availableTopic = "";
char* mqttUsername = "";
char* mqttPassword = "";
String randomId = String(random(1000, 9999), DEC);
String mqttClientId = "esp-" + randomId;
String wifiHostName = "esp-" + randomId;
//mqtt订阅topic
String* mqttSubTopics;
int mqttSubTopicsNum = 0;

//mqtt是否连接着
bool isMqttConn = 0;

//mqtt对象配置
WiFiClient espClient;
PubSubClient client(espClient);

//mqtt配置默认值
char mqtt_server[40] = "";
char mqtt_port[6] = "";
//存储值
int mqtt_port_int = 0;

//默认不保存配置内容，只有通过网页配置后回调后，才保存配置，防止重复保存
bool shouldSaveConfig = false;


//网页保存设置配置是否保存，如果没有通过网页配置，默认不保存
void saveConfigCallback () {
  Serial.println("[MyWifiMqttManager] Should save config");
  shouldSaveConfig = true;
}

//获取FS内存中的数据
void readFSConfig(){
  Serial.println("[MyWifiMqttManager] mounting FS...");
  //开始读取
  if (SPIFFS.begin()) {
    Serial.println("[MyWifiMqttManager] mounted file system");
    if (SPIFFS.exists("/config.json")) {
      //配置存在，读取并加载
      Serial.println("[MyWifiMqttManager] reading config file");
      File configFile = SPIFFS.open("/config.json", "r");
      if (configFile) {
        Serial.println("[MyWifiMqttManager] opened config file");
        size_t size = configFile.size();
        //分配缓存读取文件内容
        std::unique_ptr<char[]> buf(new char[size]);
        configFile.readBytes(buf.get(), size);
        #if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
          DynamicJsonDocument json(1024);
          auto deserializeError = deserializeJson(json, buf.get());
          serializeJson(json, Serial);
          if ( ! deserializeError ) {
        #else
          DynamicJsonBuffer jsonBuffer;
          JsonObject& json = jsonBuffer.parseObject(buf.get());
          json.printTo(Serial);
          if (json.success()) {
        #endif
            Serial.println("[MyWifiMqttManager] parsed json");
            strcpy(mqtt_server, json["mqtt_server"]);
            strcpy(mqtt_port, json["mqtt_port"]);
          } else {
            Serial.println("[MyWifiMqttManager] failed to load json config");
          }
        configFile.close();
      }
    }
  } else {
    Serial.println("[MyWifiMqttManager] failed to mount FS");
  }
}

//写入配置文件到内存
void writeFSConfig(){
  if (shouldSaveConfig) {
    Serial.println("[MyWifiMqttManager] saving config");
    #if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
        DynamicJsonDocument json(1024);
    #else
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.createObject();
    #endif
    json["mqtt_server"] = mqtt_server;
    json["mqtt_port"] = mqtt_port;
    //打开文件并写入
    File configFile = SPIFFS.open("/config.json", "w");
    if (!configFile) {
      Serial.println("[MyWifiMqttManager] failed to open config file for writing");
    }
    #if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
        serializeJson(json, Serial);
        serializeJson(json, configFile);
    #else
        Serial.print("[MyWifiMqttManager] save content: ");
        json.printTo(Serial);
        json.printTo(configFile);
    #endif
    configFile.close();
    Serial.println();
    Serial.println("[MyWifiMqttManager] config saved!");
  }
}

//初始化wifi配置
void wifiManagerInit(){
  //设置网页中需要配置的另外的参数的信息(id/name, placeholder/prompt, default length)，网页配置结束后custom_mqtt_server变量存储值
  WiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40);
  WiFiManagerParameter custom_mqtt_port("port", "mqtt port", mqtt_port, 6);
  //网页配置后是否存储配置信息，一般设置默认不保存，配置完成后设置为保存
  wifiManager.setSaveConfigCallback(saveConfigCallback);
  //设置超时，以便 ESP 不会挂起等待配置--防止断网后需要配置，但是网络恢复后无法自动联网
  wifiManager.setConfigPortalTimeout(120);
  //可以配置固定ip
  //wifiManager.setSTAStaticIPConfig(IPAddress(10, 0, 1, 99), IPAddress(10, 0, 1, 1), IPAddress(255, 255, 255, 0));
  //添加额外的参数变量到wifimanager中
  wifiManager.addParameter(&custom_mqtt_server);
  wifiManager.addParameter(&custom_mqtt_port);
  //设置信号的最小质量，使其忽略低于该质量的AP，默认为8%
  //wifiManager.setMinimumSignalQuality();
  //单位用秒，设置超时，直到配置门户被关闭，这对于使其全部重试或进入睡眠非常有用
  wifiManager.setTimeout(120);
  // 打印调试内容
  wifiManager.setDebugOutput(true);
  //获取ssid和pass并尝试连接，如果连接不上，则启动具有指定名称的接入点，然后进入一个等待配置的阻塞循环
  if (!wifiManager.autoConnect(apSsid, apPassword)) {
    Serial.println("[MyWifiMqttManager] failed to connect and hit timeout");
    delay(3000);
    //重启并再试一次或者可以设置进入深睡眠
    ESP.restart();
  }
  //到这儿说明连接wifi成功
  Serial.println("[MyWifiMqttManager] connected...yeey :)");
  //获取额外参数的值
  strcpy(mqtt_server, custom_mqtt_server.getValue());
  strcpy(mqtt_port, custom_mqtt_port.getValue());
}

//连接次数---超时断开并要求重新配置
int mqttErrorCount = 0;
//连上mqtt的操作
void mqttConnect(){
  //连接mqtt
  //boolean connect (clientID, willTopic, willQoS, willRetain, willMessage)
  bool isConnected = 0;
  //判断
  if(mqttUsername=="" && mqttPassword==""){
    isConnected = client.connect(mqttClientId.c_str(), availableTopic, 0, true, "offline");
  }else{
    isConnected = client.connect(mqttClientId.c_str(), mqttUsername, mqttPassword, availableTopic, 0, true, "offline");
  }
  if(isConnected) {
    //打印
    Serial.println("[MyWifiMqttManager] Connected to MQTT server");
    //设置连接成功
    isMqttConn = 1;
    //订阅内容
    for(int i=0; i<  mqttSubTopicsNum; i++){
      Serial.print("[MyWifiMqttManager] subscribe topic: ");
      Serial.println(mqttSubTopics[i]);
      client.subscribe(mqttSubTopics[i].c_str());
    }
    //发布复活消息
    client.publish(availableTopic, "online");
  } else {
    mqttErrorCount++;
    //如果计数错误太多，直接删除配置重启
    if(mqttErrorCount >= 50){
      if(WiFi.isConnected()){
        Serial.println("[MyWifiMqttManager] 配置的mqtt无效,无法连接mqtt,即将重启");
        //如果此时wifi连接着，说明为mqtt配置失败，重启再次连接
        ESP.restart();
      }else{
        //如果此时无法连接网络，说明网络已经断开，需要重新配网连接，但若是超时，亦能重新启动并按照旧密码连接！
        Serial.println("[MyWifiMqttManager] wifi disconncted, please reconnect it!");
        wifiManagerInit();
      }
    }
    //打印错误码
    Serial.print("[MyWifiMqttManager] Failed to connect to MQTT server, error count:");
    Serial.print(mqttErrorCount);
    Serial.print(", rc:");
    Serial.println(client.state());
    //等待1秒后重试
    delay(1000);
  }
}

//mqtt循环
void mqttLoop(){
  if(!client.connected()) {
    isMqttConn = 0;
    Serial.println("[MyWifiMqttManager] Reconnecting to MQTT server...");
    mqttConnect();
  }else{
    isMqttConn = 1;
    mqttErrorCount = 0;
  }
  client.loop();
}

//以下为对外接口：

//清除配置内容，用于测试
void clearConfig(){
  Serial.println("[MyWifiMqttManager] SPIFFS format start");
  SPIFFS.format();    // 格式化SPIFFS
  Serial.println("[MyWifiMqttManager] SPIFFS format finish");
  wifiManager.resetSettings();
  Serial.println("[MyWifiMqttManager] wifi message cleaned");
}

//可以单独设置的参数:mqttCilentId、hostname、username、password
void setWifiHostname(String hostname){
  wifiHostName = hostname;
}

void setMqttClientId(String clientId){
  mqttClientId = clientId;
}

void setMqttUser(char* username, char* password){
  mqttUsername = username;
  mqttPassword = password;
}

//初始化wifi和mqtt
void initWifiMqtt(char* myApSsid, char* myApPass, char* onlineTopic, String subTopics[], int topicNum, void (*callback)(char *, byte *, unsigned int)){
  //初始化参数
  apSsid = myApSsid;
  apPassword = myApPass;
  availableTopic = onlineTopic;
  mqttSubTopics = subTopics;
  mqttSubTopicsNum = topicNum;
  //清除缓存，用于测试
  //clearConfig();
  //先读看看是否有信息
  readFSConfig();
  //初始化wifi配置
  wifiManagerInit();
  //将网页设置的参数保存到内存中
  writeFSConfig();
  //获取值测试
  //Serial.println(WiFi.localIP());
  // Serial.println(mqtt_server);
  // Serial.println(mqtt_port);
  //更改wifi连接后的hostname--在路由器管理显示
  WiFi.hostname(wifiHostName.c_str());
  //初始化mqtt
  //先转换port为整型
  mqtt_port_int = atoi(mqtt_port);
  Serial.print("[MyWifiMqttManager] mqtt server: ");
  Serial.println(mqtt_server);
  Serial.print("[MyWifiMqttManager] mqtt port: ");
  Serial.println(mqtt_port_int);
  client.setServer(mqtt_server, mqtt_port_int);
  client.setCallback(callback);
  while (!client.connected()) {
    Serial.println("[MyWifiMqttManager] Connecting to MQTT server...");
    mqttConnect();
  }
}

//获取当前mqtt连接状态
bool mqttState(){
  return isMqttConn;
}

//获取当前wifi状态
bool wifiState(){
  return WiFi.isConnected();
}

//重置网络设置
void resetWifiMqtt(){
  //清除配置
  clearConfig();
  //重启
  ESP.restart();
}